using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics.Dynamics;
using FarseerPhysics;

namespace Urban_Flash.Dependencies
{
    public partial class Camera2D : PhysicsObject
    {
        //TODO: when camera reaches max lenegth away form the player stop moving
        #region Fields
        public Vector2 Origin { get; set; }
        public float Rotation { get; set; }
        private readonly Viewport _viewport;
        protected float _zoom;
        private Rectangle? _limits;
        #endregion

        #region Constructors
        public Camera2D(Viewport viewport, World physics)
            : base(physics, new Vector2(832, -145), 1, 1, 0.0f)
        {
            _viewport = viewport;
            Origin = new Vector2(viewport.Width / 2.0f, viewport.Height / 2.0f);
            Zoom = 1.0f;
            Rotation = 0.0f;
            body.IgnoreGravity = true;
            body.OnCollision += Body_OnCollision;
        }
        #endregion

        #region Properties
        //protected Vector2 _position;
        public Vector2 CameraPosition 
        { 
            get { return Position; }
            set
            {
                Position = value;

                // If there's a limit set and the camera is not transformed clamp position to limits
                if (Limits != null && Zoom == 1.0f && Rotation == 0.0f)
                {
                    float X = MathHelper.Clamp(this.Position.X, Limits.Value.X, Limits.Value.X + Limits.Value.Width - _viewport.Width);
                    float Y = MathHelper.Clamp(this.Position.Y, Limits.Value.Y, Limits.Value.Y + Limits.Value.Height - _viewport.Height);
                    Position = new Vector2(X, Y);
                }
            } 
        }

        public float Zoom
        {
            get { return _zoom; }
            set { _zoom = value; if (_zoom < 0.1f) _zoom = 0.1f; } // Negative zoom will flip image
        }

        public void LookAt(Vector2 position)
        {
            Position = position - new Vector2(_viewport.Width / 2.0f, _viewport.Height / 2.0f);
        }

        // Auxiliary function to move the camera
        public void Move(Vector2 amount)
        {
            Position += amount;
        }

        public Rectangle? Limits // View - Camera Boundaries
        {
            get { return _limits; }
            set
            {
                if (value != null)
                {
                    // Assign limit but make sure it's always bigger than the viewport
                    _limits = new Rectangle
                    {
                        X = value.Value.X,
                        Y = value.Value.Y,
                        Width = System.Math.Max(_viewport.Width, value.Value.Width),
                        Height = System.Math.Max(_viewport.Height, value.Value.Height)
                    };

                    // Validate camera position with new limit
                    Position = Position;
                }
                else
                {
                    _limits = null;
                }
            }
        }
        #endregion

        #region Methods
        public Matrix GetViewMatrix(Vector2 parallax)
        {
            //Fix zoom and rotation to be around the center
            bool CenterCamPosition = true;

            if (CenterCamPosition == false)
            {
                // To add parallax, simply multiply it by the position
                return Matrix.CreateTranslation(new Vector3(-ConvertUnits.ToDisplayUnits(Position) * parallax, 0.0f)) *
                       Matrix.CreateTranslation(new Vector3(-Origin, 0.0f)) *
                       Matrix.CreateRotationZ(Rotation) *
                       Matrix.CreateScale(Zoom, Zoom, 1) *
                       Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
            }
            else
            {
                //Make the camera poistion the center  of the screen : Convert to Display units since positon is in meters
                return Matrix.CreateTranslation(new Vector3(-ConvertUnits.ToDisplayUnits(Position) * parallax, 0.0f)) *
                       Matrix.CreateTranslation(new Vector3(Origin, 0.0f)) *
                       Matrix.CreateRotationZ(Rotation) *
                       Matrix.CreateScale(Zoom, Zoom, 1);
            }
        }

        public Matrix GetViewMatrixSimUnits(Vector2 parallax)
        {
            //Fix zoom and rotation to be around the center
            bool CenterCamPosition = true;

            if (CenterCamPosition == false)
            {
                // To add parallax, simply multiply it by the position : Convert Orgin to meters
                return Matrix.CreateTranslation(new Vector3(-Position * parallax, 0.0f)) *
                       Matrix.CreateTranslation(new Vector3(-ConvertUnits.ToSimUnits(Origin), 0.0f)) *
                       Matrix.CreateRotationZ(Rotation) *
                       Matrix.CreateScale(Zoom, Zoom, 1) *
                       Matrix.CreateTranslation(new Vector3(ConvertUnits.ToSimUnits(Origin), 0.0f));
            }
            else
            {
                //Make the camera poistion the center  of the screen : Convert Orgin to meters
                return Matrix.CreateTranslation(new Vector3(-Position * parallax, 0.0f)) *
                       Matrix.CreateTranslation(new Vector3(ConvertUnits.ToSimUnits(Origin), 0.0f)) *
                       Matrix.CreateRotationZ(Rotation) *
                       Matrix.CreateScale(Zoom, Zoom, 1);
            }
        }

        bool Body_OnCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
        {
            // fixtureA: The fixture of 'Body'
            // fixtureB: The fixture of the body that collided with 'Body'

            // return true : acknowledge the collision
            // return false : ignore the collision the collision
            return false;
        }
        #endregion
    }

}
